#ifndef __ond_loader__
#define __ond_loader__

#include <algorithm>

struct ond_loader_t
{
	enum 
	{
		OND_MARK = 'FDNO',
		OPT_LOD_NAMES = 1,
	};


	struct ond_file_header
	{
		uint32_t	mark;	
		uint32_t	count;
	};

	struct api_desc_in_file_32
	{
		uint32_t	rva;
		uint8_t		ret_dwords;
		char		type;
		uint8_t		name_len;
		uint8_t		forw_len;
		uint32_t	name_off;
		uint32_t	forw_off;

		static bool less( const api_desc_in_file_32 & left, const api_desc_in_file_32 & right )
		{
			return left.rva < right.rva;
		}
	};

	UTIL::sentry<uint8_t*>	_buffer;

	api_desc_in_file_32 *	_item_begin;
	api_desc_in_file_32 *	_item_end;
	size_t					_load_opt;

	const api_desc_in_file_32 * find_by_rva( uint32_t rva )
	{
		api_desc_in_file_32 key = { rva };
		api_desc_in_file_32 * p = std::lower_bound( _item_begin, _item_end, key, api_desc_in_file_32::less );
		if( p == _item_end || p->rva != rva ) return NULL;
		return p;
	}

	const char * get_name( const api_desc_in_file_32 * desc )
	{
		if( _load_opt & OPT_LOD_NAMES )
			return (const char *)(_buffer + desc->name_off);
		return "(unknown)";
	}
	const char * get_forword_name( const api_desc_in_file_32 * desc )
	{
		if( !desc->forw_len ) return NULL;
		if( _load_opt & OPT_LOD_NAMES )
			return (const char *)(_buffer + desc->forw_off);
		return "(unknown)";
	}

	long open( const char * file, size_t opt = 0 )
	{
		xm::iptr<xm::IKNativeFile> fo( xm::PROGID_WinNativeFile );
		if( !fo ) return E_FAIL;

		RFAILED( fo->Open( file, OFR ) );

		xm::iptr<xm::IKBStream> stm(fo);

		size_t eff = 0;

		ond_file_header fhdr;
		RFAILED( stm->Readp( 0, &fhdr, sizeof(fhdr), &eff ) );
		if( eff != sizeof(fhdr) ) return E_FAIL;

		if( fhdr.mark != OND_MARK ) return E_FAIL;

		uint64_t flen = stm->Length();

		uint64_t name_offset = sizeof(fhdr) + sizeof(api_desc_in_file_32) * fhdr.count;
		if( name_offset > flen ) return E_FAIL;

		uint64_t next_name_start = name_offset;

		for( size_t i = 0; i < fhdr.count; ++ i )
		{
			api_desc_in_file_32 desc ;
			RFAILED( stm->Read( &desc, sizeof(desc), &eff ) );
			if( eff != sizeof(desc) ) return E_FAIL;
			if( desc.name_off != next_name_start ) return E_FAIL;

			next_name_start += (desc.name_len + 1);
			if( opt & OPT_LOD_NAMES )
				if( next_name_start > flen ) 
					return E_FAIL;

			if( desc.forw_off )
			{
				if( desc.forw_off != next_name_start ) return E_FAIL;
				next_name_start += (desc.forw_len + 1);

				if( opt & OPT_LOD_NAMES )
					if( next_name_start > flen ) return E_FAIL;
			}
		}

		if( opt & OPT_LOD_NAMES )
			if( next_name_start != flen )
				return E_FAIL;

		size_t read_end = (opt & OPT_LOD_NAMES) ? next_name_start : name_offset;

		_buffer = new uint8_t[ read_end ];
		if (!_buffer) return E_OUTOFMEMORY;

		RFAILED( stm->Readp( 0, _buffer.ptr(), read_end, &eff ) );
		if( eff != read_end ) return E_FAIL;

		_item_begin = (api_desc_in_file_32*)(_buffer + sizeof(ond_file_header) );
		_item_end = _item_begin + fhdr.count;
		_load_opt = opt;

		return S_OK;
	}

	
};


#endif